﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;

// Khai báo các thuộc tính của nhân vật chính
// Đồng thời xử lý các hành động của nhân vật chính
namespace MyGames
{
    class Player : Character
    {
        #region Declaration
        // Level
        int level;
        int exp;
        int nextLevelExp;

        // Stats
        int strength; // suc manh
        int agility; // do nhanh nhen
        int intelligent; // do thong minh
        int skillPoint;

        // Attributes
        float asRate;
        float expRate;
        float luck;
        float critChance;

        // Activities
        bool attacking = false;
        bool finishAttacked = false;
        bool dead = false;
        float nextFinishAttack;
        float baseAS;

        // Inventory, Questlog
        //Bag _bag;
        //Equipment _equipments;
        int gold;
        #endregion

        #region Properties

        public int Level
        {
            get { return level; }
            set
            {
                // Level up is harder by level
                if (value >= level)
                {
                    if (level < 10) nextLevelExp = 100 + 20 * level;
                    else
                    {
                        // Next level exp = 200 x (a + 2) x 2^a
                        int a = (level - 10) / 20;
                        nextLevelExp = (int)(200 * Math.Pow(2d, a + 1) + (level - 10) * 10 * Math.Pow(2d, a));
                      
                    }

                    level = value;
                }
            }
        }

        public int Experience
        {
            get { return exp; }
            set
            {
                exp = value;
                if (exp >= nextLevelExp)
                {
                    //AudioManager.PlayCue("LevelUp");

                    exp = 0;
                    Level++;

                    // Level up, 
                    // sức mạnh +2, độ nhanh nhẹn +1, thông minh +1, HP +30, MP +10, sức tấn công +5,
                    // Skill Point +1
                    // Nạp đầy HP và MP
                    SkillPoint++;
                    Strength += 2;
                    Agility += 1;
                    Intelligent += 1;
                    MaxHealth += 30;
                    MaxMana += 10;
                    AttackPower += 5;
                    Health = MaxHealth;
                    Mana = MaxMana;
                }
            }
        }

        public int NextLevelExp
        {
            get { return nextLevelExp; }
        }

        public float ExperienceRate
        {
            get { return expRate; }
            set { expRate = value; }
        }

        public int Gold
        {
            get { return gold; }
            set { gold = value; }
        }

        public float Luck
        {
            get { return luck; }
            set { luck = value; }
        }

        public int SkillPoint
        {
            get { return skillPoint; }
            set { skillPoint = Math.Max(0, value); }
        }

        public int Strength
        {
            get { return strength; }
            set
            {
                // Each point in strength increase
                // +25 HP, +1 AP, + 0.01% HR
                int diff = value - strength;

                AttackPower += diff;
                HealthRegen += diff / 10000f;
                MaxHealth += diff * 25;
                strength = value;
            }
        }

        public int Agility
        {
            get { return agility; }
            set
            {
                // Each agi increase
                // +0.1% AS, and 20 agi increase +1 Armor
                int diff = value - agility;

                AttackRate += diff * 0.0001f;
                //Armor = agility / 50;

                agility = value;
            }
        }

        public int Intelligent
        {
            get { return intelligent; }
            set
            {
                int diff = value - intelligent;

                ManaRegen += diff / 7500f;
                MaxMana += 15 * diff;
                Mana += 15 * diff;
                intelligent = value;
            }
        }

        public new float AttackSpeed
        {
            get { return attackSpeed; }
        }

        public float AttackRate
        {
            get { return asRate; }
            set
            {
                asRate = value;
                attackSpeed = baseAS / asRate;

                animations["Attack"].FrameLength =
                animations["attackchain1"].FrameLength = attackSpeed/animations["Attack"].FrameCount;
                //animations["attackchain2"].FrameLength = animations["attackchain3"].FrameLength =
                //animations["attackchain4"].FrameLength = animations["attackchain5"].FrameLength =
                //animations["attackchain6"].FrameLength = animations["attackchain7"].FrameLength =
                //animations["attackchain8"].FrameLength = _attackSpeed / animations["Attack"].FrameCount;
            }
        }

        public float CriticalChance
        {
            get { return critChance; }
            set { critChance = value; }
        }

        public bool Attacking
        {
            get { return attacking; }
        }

        public bool FinishAttacking
        {
            get { return finishAttacked; }
        }

        public bool FacingLeft
        {
            get { return flipped; }
        }

        public bool Dead
        {
            get { return dead; }
        }

        //public Bag Bag
        //{
        //    get { return _bag; }
        //}

        //public Equipment Equipment
        //{
        //    get { return _equipments; }
        //}

        #endregion

        #region Construtor
        public Player()
        {
            // Get type and path
            int itype = (int)ModelType.Phodom;
            string path = Const.TexturePathCharacter + ModelInfomation.CharacterInformation[itype].Name + "\\";
            FrameInformation objInf = ModelInfomation.FrameInformation[itype];
            CharacterInformation baseInf = ModelInfomation.CharacterInformation[itype];

            #region Animation
            // Init object value
            enabled = true;
            ContentManager mgr = GameLoader.Content;

            // Animation strips
            animations.Add("idle", new AnimationStrip(mgr.Load<Texture2D>(path + "Idle"), 1, 4, "idle"));
            animations.Add("walk", new AnimationStrip(mgr.Load<Texture2D>(path + "Walk"), 1, 4, "walk"));
            animations.Add("run", new AnimationStrip(mgr.Load<Texture2D>(path + "Run"), 1, 4, "run"));

            animations.Add("gethit", new AnimationStrip(mgr.Load<Texture2D>(path + "Gethit"), 1, 1, "gethit"));
            animations.Add("die", new AnimationStrip(mgr.Load<Texture2D>(path + "Die"), 1, 2, "die"));
         
            animations.Add("Attack", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack1"), 1, 8, "Attack"));
            //animations.Add("attack_double", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack1"), 1, 8, "Attack"));
            animations.Add("attackchain1", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack2"), 1, 6, "attackchain1"));
            //animations.Add("attackchain2", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack3"), 1, 5, "attackchain2"));
            //animations.Add("attackchain3", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack4"), 1, 5, "attackchain3"));
            //animations.Add("attackchain4", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack5"), 1, 5, "attackchain4"));
            //animations.Add("attackchain5", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack6"), 1, 5, "attackchain5"));
            //animations.Add("attackchain6", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack7"), 1, 4, "attackchain6"));
            //animations.Add("attackchain7", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack8"), 1, 5, "attackchain7"));
            //animations.Add("attackchain8", new AnimationStrip(mgr.Load<Texture2D>(path + "Attack9"), 1, 4, "attackchain8"));

            animations["idle"].LoopAnimation = true; 
            animations["idle"].FrameLength = 0.2f;

            animations["walk"].LoopAnimation = true;
            animations["run"].LoopAnimation = true;

            animations["gethit"].NextAnimation = "idle";

            animations["Attack"].NextAnimation = "attackchain1";
            //animations["attackchain1"].NextAnimation = "attackchain2";
            //animations["attackchain2"].NextAnimation = "attackchain3";
            //animations["attackchain3"].NextAnimation = "attackchain4";
            //animations["attackchain4"].NextAnimation = "attackchain5";
            //animations["attackchain5"].NextAnimation = "attackchain6";
            //animations["attackchain6"].NextAnimation = "attackchain7";
            //animations["attackchain7"].NextAnimation = "attackchain8";
            animations["attackchain1"].NextAnimation = "Attack";

            PlayAnimation("idle");
            #endregion

            #region Base Information
            // Character information
            health = maxHealth = baseInf.MaxHealth;
            mana = maxMana = baseInf.MaxMana;
            attackPower = baseInf.AttackPower;
            armor = baseInf.Armor;
            speed = baseInf.MoveSpeed;
            baseAS = baseInf.AttackSpeed;
            healthRegen = baseInf.HealthRegen;
            manaRegen = baseInf.ManaRegen;

            // Hero information
            Strength = Agility = Intelligent = 20;
            Level = 1;
            skillPoint = 1;
            expRate = 1f;
            luck = 1f;
            critChance = 0.1f; // 10%
            AttackRate = 1f;
            #endregion

        }
        #endregion

        #region Update and Draw
        private void UpdateInput(GameTime gameTime, ref string newAnimation)
        {
            float time = (float)gameTime.TotalGameTime.TotalSeconds;

            #region Attack
    
            if (EmuGamePad.IsEmuKeyPressed(EmuButton.Attack))
            {
                // New Attack
                if (!attacking)
                {
                    attacking = true;
                    newAnimation = "Attack";
                    nextFinishAttack = time + attackSpeed;
                }
                else
                    newAnimation = currentAnimation;

                if (time > nextFinishAttack)
                {
                    finishAttacked = true;
                    nextFinishAttack = time + attackSpeed;
                }
            }

            if (newAnimation == "idle") attacking = false;
            #endregion

            #region Walk
            if (EmuGamePad.IsEmuKeyPressed(EmuButton.Walk))
            {
                attacking = false;
                newAnimation = "walk";
                velocity = speed * EmuGamePad.GetDirection(gameTime);
                velocity.Y *= 0.5f;
                if (velocity.X < 0) flipped = true;
                if (velocity.X > 0) flipped = false;
            }
            #endregion

            #region Run
            if (EmuGamePad.IsEmuKeyPressed(EmuButton.Run))
            {
                attacking = false;
                newAnimation = "run";
                velocity = speed * EmuGamePad.GetDirection(gameTime);
                velocity.Y *= 0.5f;
                if (velocity.X < 0) flipped = true;
                if (velocity.X > 0) flipped = false;
            }
            #endregion
        }

        public override void Update(GameTime gameTime)
        {
            float time = (float)gameTime.TotalGameTime.TotalSeconds;
            string newAnimation = "idle";
            velocity = Vector2.Zero;
            finishAttacked = false;

            // ==================== Die or Gethit =======================
            #region Dying
            // Check live or dead
            if (Health <= 0)
            {
                velocity = Vector2.Zero;
                if (currentAnimation != "die")
                {
                    PlayAnimation("die");
                    nextActionTime = (float)gameTime.TotalGameTime.TotalSeconds + 5f;
                }

                if (animations["die"].FinishedPlaying && gameTime.TotalGameTime.TotalSeconds > nextActionTime)
                    dead = true;

                base.Update(gameTime);
                return;
            }
            #endregion

            #region GetHit
            if (gethit)
            {
                if (currentAnimation != "gethit")
                {
                    attacking = finishAttacked = false;
                    PlayAnimation("gethit", false, true, false);
                    nextActionTime = (float)gameTime.TotalGameTime.TotalSeconds + 0.3f;
                }

                if (time > nextActionTime)
                {
                    gethit = false;
                    invicible = true;
                    invicibleEndTime = time + 1f;
                    PlayAnimation("idle");
                }

                base.Update(gameTime);
                return;
            }

            if (time > invicibleEndTime) invicible = false;
            #endregion

            // ==================== Process Input =======================
            UpdateInput(gameTime, ref newAnimation);

            // ==================== Process Update ======================
            // Update animation
            if (newAnimation != currentAnimation)
                PlayAnimation(newAnimation);

            // Update camera
            RepositionCamera();

            // <Other information>
            base.Update(gameTime);
        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            // Draw playerbar
            //Vector2 pos = GUI.BarGUI.PlayerBar.position;
            //spriteBatch.Draw(GUI.BarGUI.PlayerBar.container, pos, null, Color.White,
            //    0f, Vector2.Zero, 1f, SpriteEffects.None, 0.99f);

            Vector2 pos = GUI.BarGUI.PlayerBar.position;
            spriteBatch.Draw(GUI.BarGUI.PlayerBar.fullBar, pos, null, Color.White,
                0f, Vector2.Zero, 1f, SpriteEffects.None, 0.99f);

            // Draw health, mana and exp
            spriteBatch.Draw(GUI.BarGUI.PlayerBar.health, new Vector2(pos.X + 65, pos.Y + 13), null, Color.White,
                0f, Vector2.Zero, new Vector2(health / maxHealth, 1f), SpriteEffects.None, 1f);
            spriteBatch.Draw(GUI.BarGUI.PlayerBar.mana, new Vector2(pos.X + 65, pos.Y + 18), null, Color.White,
                0f, Vector2.Zero, new Vector2(mana / maxMana, 1f), SpriteEffects.None, 1f);
            spriteBatch.Draw(GUI.BarGUI.PlayerBar.experience, new Vector2(pos.X + 88, pos.Y + 27), null, Color.White,
                0f, Vector2.Zero, new Vector2(exp * 1f / nextLevelExp, 1f), SpriteEffects.None, 1f);

            base.Draw(spriteBatch);
        }
        #endregion

        #region Helper Methods
        private void RepositionCamera()
        {
            worldLocation = new Vector2(worldLocation.X, MathHelper.Clamp(worldLocation.Y, Const.ViewportTopMost, Const.ViewportBottomMost));
            Vector2 screenPos = Camera.WorldToScreen(worldLocation);

            if (screenPos.X > Const.ViewportRightMost)
                Camera.Move(new Vector2(screenPos.X - Const.ViewportRightMost, 0));

            if (screenPos.X < Const.ViewportLeftMost)
                Camera.Move(new Vector2(screenPos.X - Const.ViewportLeftMost, 0));
        }
        #endregion

        #region Public methods

        public void GetExperience(int value)
        {
            Experience += (int)(value * expRate);
        }

        public void GetDamage(int dam)
        {
            //AudioManager.PlayCue("DamageNormal");

            // player get 90% - 110% dam
            dam = (int)((RandomHelper.rand.NextDouble() / 5 + 0.9f) * dam);
            health -= dam * (1 - armor / 100);
            gethit = true;
        }

        public void SavePlayerData()
        {
            PlayerData data = new PlayerData()
            {
                WorldLocation = worldLocation,
                MaxHealth = maxHealth,
                MaxMana = maxMana,
                Health = health,
                Mana = mana,
                AttackPower = attackPower,
                Armor = armor,
                Speed = speed,
                AttackSpeed = attackSpeed,
                HealthRegen = healthRegen,
                ManaRegen = manaRegen,

                Level = level,
                Exp = exp,
                NextLevelExp = nextLevelExp,
                Strength = strength,
                Agility = agility,
                Intelligent = intelligent,
                SkillPoint = skillPoint,

                ASRate = asRate,
                ExpRate = expRate,
                Luck = luck,
                CritChange = critChance,
            };

            //return data;
        }

        struct PlayerData
        {
            public Vector2 WorldLocation;
            public float MaxHealth;
            public float MaxMana;
            public float Health;
            public float Mana;
            public int AttackPower;
            public int Armor;
            public float Speed;
            public float AttackSpeed;
            public float HealthRegen;
            public float ManaRegen;

            public int Level;
            public int Exp;
            public int NextLevelExp;
            public int SkillPoint;
            public int Strength;
            public int Agility;
            public int Intelligent;

            public float ASRate;
            public float ExpRate;
            public float Luck;
            public float CritChange;
        }

        #endregion
    }
}
